/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
* DNA_RegionUtil.java
*
* Created on December 31, 2007, 11:05 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
 */
package genomemap.data;

import genomemap.worker.HammingDistance;
import genomemap.worker.RandomCost;

import javautil.collections.ArrayUtil;

import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Vector;

/**
 * @author stewari1
 */
public class DNA_RegionUtil {

    public static void main(String[] args) {}

    /**
     * gets the contig counts when contigs are of probes`
     * requires: hybridData and order
     * @param ch_id
     * @param hybridData
     * @param probeOrder
     * @return
     */
    public static int getProbeContigCount(int ch_id, int[] probeOrder) {

        PDataSource pData     = DataSource.getPDataSource(ch_id);
        int[][] binaryProfile = ArrayUtil.transpose(pData.getHybridData());

        return getContigCount(probeOrder, binaryProfile);
    }

    /**
     * gets the contig counts when contigs are created from clones
     * @param hybridData
     * @param cloneOrder
     * @return int contig counts for clones
     */
    public static int getCloneContigCount(int ch_id, int[] cloneOrder) {

        PDataSource pData = DataSource.getPDataSource(ch_id);

        return getContigCount(cloneOrder, pData.getHybridData());
    }

    /**
     * <code>order</code> could be of probes or clones and the <code>binaryProfile</code>
     * must have then probes or clones as rows and the other one in columns.
     * A contig is an island (PNAS Vol. 91, pp. 11094-11098) with two or more contigs.
     * @param order order of rows in <code>binaryProfile</code>
     * @param binaryProfile hybridization data
     * @return
     */
    public static int getContigCount(int[] order, int[][] binaryProfile) {

        int contigCount = 0;

        for (int rowIndex = 0; rowIndex < order.length - 1; rowIndex++) {

            if (!isConnected(rowIndex, order, binaryProfile)) {
                contigCount++;
            }
        }

        return contigCount;
    }

    /**
     * used by {@link #getContigCount(int[],int[][])}
     * tests if the row is connected to its following row. Here row could
     * mean a probe/clone index
     * @param rowIndex clone/probe index
     * @param order order of probe/clone
     * @param binaryProfile hybridization data
     * @return true if connected, false otherwise
     */
    public static boolean isConnected(int rowIndex, int[] order, int[][] binaryProfile) {

        boolean connected = false;

        for (int i = 0; i < binaryProfile[0].length; i++) {

            if ((binaryProfile[order[rowIndex] - 1][i] == 1)
                    && (binaryProfile[order[rowIndex + 1] - 1][i] == 1)) {

                connected = true;

                break;
            }
        }

        return connected;
    }

    /**
     * used by {@link #getContigCount(int[],int[][])}
     * counts the number of hybridizations in a single row at the specified
     * <code>rowIndex</code>. Count 1 designates an anchored island whereas
     * count 2 or onwards constitutes a contig.
     * @param rowIndex
     * @param order
     * @param binaryProfile
     * @return
     */
    public static int anchoredCount(int rowIndex, int[] order, int[][] binaryProfile) {

        int anchoredCount = 0;

        for (int i = 0; i < binaryProfile[0].length; i++) {

            if (binaryProfile[order[rowIndex] - 1][i] == 1) {
                anchoredCount++;
            }
        }

        return anchoredCount;
    }

    public static int getLowerBoundContigCount(int ch_id, int[] probeOrder) {

        PDataSource pDataSource = DataSource.getPDataSource(ch_id, probeOrder);
        int[][] hybridData      = pDataSource.getHybridData();
        int count1              = 0;
        int contigCount         = 0;

        for (int probeIndex = 0; probeIndex < probeOrder.length - 1; probeIndex++) {

            count1 = 0;

            for (int cloneIndex = 0; cloneIndex < hybridData.length; cloneIndex++) {

                if ((hybridData[cloneIndex][probeOrder[probeIndex] - 1] == 1)
                        && (hybridData[cloneIndex][probeOrder[probeIndex + 1] - 1] == 1)) {

                    count1++;

                    break;
                }
            }

            if (count1 == 0) {
                contigCount++;
            }
        }

        return contigCount;
    }

    /**
     * Note that both sets will have their elements potentially enriched
     * with tagging information
     * @param cloneSet
     * @param geneSet
     */
    public static void tagGenesAndClones(Set<Clone> cloneSet, Set<Gene> geneSet) {

        for (Iterator<Clone> itrClone = cloneSet.iterator(); itrClone.hasNext(); ) {

            Clone clone = itrClone.next();

            for (Iterator<Gene> itrGene = geneSet.iterator(); itrGene.hasNext(); ) {

                Gene gene = itrGene.next();

                if (gene.hasSeqInfo() && gene.isOverLapping(clone)) {
                    clone.addTaggedGene(gene);
                }
            }
        }
    }

    /**
     * clones will potentially have their <CODE>probe</CODE> property set
     * to <CODE>true</CODE> if it`s identified as a probe from the
     * <CODE>probeSet</CODE>.
     * @param cloneSet
     * @param probeSet
     */
    public static void identifyClonesAsProbes(Set<Clone> cloneSet, Set<Probe> probeSet) {

        for (Iterator<Clone> itrClone = cloneSet.iterator(); itrClone.hasNext(); ) {

            Clone clone = itrClone.next();

            for (Iterator<Probe> itrProbe = probeSet.iterator(); itrProbe.hasNext(); ) {

                Probe probe = itrProbe.next();

                if (clone.isProbe(probe)) {

                    clone.setProbe(true);

                    break;
                }
            }
        }
    }

    /**
     *
     * @param ch_id
     * @param geneOrder
     * @return
     * @deprecated it loads data for the chromosome. Use when you are not loading
     * data many times. This is replaced by
     * {@link #getGeneLabels(LinkedHashSet<Gene>,int[])}
     */
    public static LinkedHashSet<String> getGeneLabels(int ch_id, int[] geneOrder) {

        GDataSource gData                     = DataSource.getGDataSource(ch_id);
        LinkedHashSet<Gene> fullGeneSet       = gData.getFullGeneSet();
        LinkedHashSet<String> reqGeneLabelSet = new LinkedHashSet<>();

        for (int geneIndex = 0; geneIndex < geneOrder.length; geneIndex++) {

            Iterator<Gene> itr = fullGeneSet.iterator();

            while (itr.hasNext()) {

                Gene gene = itr.next();

                if (gene.getOrder() == geneOrder[geneIndex]) {

                    reqGeneLabelSet.add(gene.getLabel());

                    break;
                }
            }
        }

        return reqGeneLabelSet;
    }

    public static LinkedHashSet<String> getGeneLabels(LinkedHashSet<Gene> fullGeneSet,
            int[] geneOrder) {

        LinkedHashSet<String> reqGeneLabelSet = new LinkedHashSet<>();

        for (int geneIndex = 0; geneIndex < geneOrder.length; geneIndex++) {

            Iterator<Gene> itr = fullGeneSet.iterator();

            while (itr.hasNext()) {

                Gene gene = itr.next();

                if (gene.getOrder() == geneOrder[geneIndex]) {

                    reqGeneLabelSet.add(gene.getLabel());

                    break;
                }
            }
        }

        return reqGeneLabelSet;
    }

    /**
     *
     * @param ch_id
     * @param probeOrder
     * @return
     * @deprecated it loads data for the chromosome. Use when you are not loading
     * data many times. This is replaced by
     * {@link #getProbeLabels(LinkedHashSet<Probe>,int[])}
     */
    public static LinkedHashSet<String> getProbeLabels(int ch_id, int[] probeOrder) {

        PDataSource pData                      = DataSource.getPDataSource(ch_id);
        LinkedHashSet<Probe> fullProbeSet      = pData.getFullProbeSet();
        LinkedHashSet<String> reqProbeLabelSet = new LinkedHashSet<>();

        for (int probeIndex = 0; probeIndex < probeOrder.length; probeIndex++) {

            Iterator<Probe> itr = fullProbeSet.iterator();

            while (itr.hasNext()) {

                Probe probe = itr.next();

                if (probe.getOrder() == probeOrder[probeIndex]) {

                    reqProbeLabelSet.add(probe.getLabel());

                    break;
                }
            }
        }

        return reqProbeLabelSet;
    }

    /**
     *
     * @param fullProbeSet
     * @param probeOrder
     * @return
     */
    public static LinkedHashSet<String> getProbeLabels(LinkedHashSet<Probe> fullProbeSet,
            int[] probeOrder) {

        LinkedHashSet<String> reqProbeLabelSet = new LinkedHashSet<>();

        for (int probeIndex = 0; probeIndex < probeOrder.length; probeIndex++) {

            Iterator<Probe> itr = fullProbeSet.iterator();

            while (itr.hasNext()) {

                Probe probe = itr.next();

                if (probe.getOrder() == probeOrder[probeIndex]) {

                    reqProbeLabelSet.add(probe.getLabel());

                    break;
                }
            }
        }

        return reqProbeLabelSet;
    }

    /**
     *
     * @param ch_id
     * @param probeOrder
     * @return
     * @deprecated it loads data for the chromosome. Use when you are not loading
     * data many times. This is replaced by
     * {@link #getProbeSeqOrder(LinkedHashSet<Probe>)}
     */
    public static int[] getProbeSeqOrder(int ch_id, int[] probeOrder) {

        PDataSource pData                     = DataSource.getPDataSource(ch_id, probeOrder);
        LinkedHashSet<Probe> probeSet         = pData.getProbeSet();
        TreeMap<Integer, Integer> posOrderMap = new TreeMap<>();
        Iterator<Probe> itr                   = probeSet.iterator();

        while (itr.hasNext()) {

            Probe probe = itr.next();

            if (probe.hasSeqInfo()) {
                posOrderMap.put(new Integer(probe.getStartPos()), new Integer(probe.getOrder()));
            }
        }

        return ArrayUtil.getIntegerArray(
            (new LinkedHashSet<>(posOrderMap.values())).toArray(new Integer[0]));
    }

    /**
     * You can create sequence based probe order for a particular probe order or for the
     * entire probe set in a chromosome. You just have to pass (create) the <code>probeSet</code>
     * accordingly. For example, to pass for a particular probe order (in <code>pData</code>)use
     * <code>pData.getProbeSet()</code> or for all probes available use
     * <code>pData.getFullProbeSet()</code>
     * @param probeSet
     * @return
     */
    public static int[] getProbeSeqOrder(LinkedHashSet<Probe> probeSet) {

        TreeMap<Integer, Integer> posOrderMap = new TreeMap<>();
        Iterator<Probe> itr                   = probeSet.iterator();

        while (itr.hasNext()) {

            Probe probe = itr.next();

            if (probe.hasSeqInfo()) {
                posOrderMap.put(new Integer(probe.getStartPos()), new Integer(probe.getOrder()));
            }
        }

        return ArrayUtil.getIntegerArray(
            (new LinkedHashSet<>(posOrderMap.values())).toArray(new Integer[0]));
    }

    public static int[] getGeneSeqOrder(int ch_id, int[] geneOrder) {

        GDataSource gData                     = DataSource.getGDataSource(ch_id, geneOrder);
        LinkedHashSet<Gene> geneSet           = gData.getGeneSet();
        TreeMap<Integer, Integer> posOrderMap = new TreeMap<>();
        Iterator<Gene> itr                    = geneSet.iterator();

        while (itr.hasNext()) {

            Gene gene = itr.next();

            if (gene.hasSeqInfo()) {
                posOrderMap.put(new Integer(gene.getStartPos()), new Integer(gene.getOrder()));
            }
        }

        return ArrayUtil.getIntegerArray(
            (new LinkedHashSet<>(posOrderMap.values())).toArray(new Integer[0]));
    }

    public static int[] getGeneSeqOrder(LinkedHashSet<Gene> geneSet) {

        TreeMap<Integer, Integer> posOrderMap = new TreeMap<>();
        Iterator<Gene> itr                    = geneSet.iterator();

        while (itr.hasNext()) {

            Gene gene = itr.next();

            if (gene.hasSeqInfo()) {
                posOrderMap.put(new Integer(gene.getStartPos()), new Integer(gene.getOrder()));
            }
        }

        return ArrayUtil.getIntegerArray(
            (new LinkedHashSet<>(posOrderMap.values())).toArray(new Integer[0]));
    }
}
